package org.toonWatch

import groovy.text.SimpleTemplateEngine

import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
import org.codehaus.groovy.grails.plugins.springsecurity.NullSaltSource
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
import org.codehaus.groovy.grails.plugins.springsecurity.ui.RegistrationCode

class RegisterController extends grails.plugins.springsecurity.ui.RegisterController {
    static defaultAction = 'index'

    def mailService
    def saltSource

    def index = {
        [command: new RegisterCommand()]
    }

    def register = { RegisterCommand command ->

        if (command.hasErrors()) {
            chain (controller:'login', action:'auth', model: [command: command], params:[active:1])
            return
        }
        def user = lookupUserClass().newInstance(email: command.email, username: command.email,
            password: command.password, accountLocked: true, enabled: true, 
            name: command.name, lastname: command.lastname, address1: command.address1,
            address2: command.address2, postal: command.postal, 
            phonenumber: command.phonenumber)
        
        if (!user.validate() || !user.save()) {
            // TODO
        }

        def registrationCode = new RegistrationCode(username: user.username).save()
        String url = generateLink('verifyRegistration', [t: registrationCode.token])

        def conf = SpringSecurityUtils.securityConfig
        def body = conf.ui.register.emailBody
        if (body.contains('$')) {
            body = evaluate(body, [user: user, url: url])
        }
        mailService.sendMail {
            to command.email
            from conf.ui.register.emailFrom
            subject conf.ui.register.emailSubject
            html body.toString()
        }

        render view: 'index', model: [emailSent: true]
    }

    def verifyRegistration = {

        def conf = SpringSecurityUtils.securityConfig
        String defaultTargetUrl = conf.successHandler.defaultTargetUrl

        String token = params.t

        def registrationCode = token ? RegistrationCode.findByToken(token) : null
        if (!registrationCode) {
            flash.error = message(code: 'spring.security.ui.register.badCode')
            redirect uri: defaultTargetUrl
            return
        }

        def user
        RegistrationCode.withTransaction { status ->
            user = lookupUserClass().findByUsername(registrationCode.username)
            if (!user) {
                return
            }
            user.accountLocked = false
            user.save(flush:true)
            def UserRole = lookupUserRoleClass()
            def Role = lookupRoleClass()
            for (roleName in conf.ui.register.defaultRoleNames) {
                UserRole.create user, Role.findByAuthority(roleName)
            }
            registrationCode.delete()
        }

        if (!user) {
            flash.error = message(code: 'spring.security.ui.register.badCode')
            redirect uri: defaultTargetUrl
            return
        }

        springSecurityService.reauthenticate user.username

        flash.message = message(code: 'spring.security.ui.register.complete')
        redirect uri: conf.ui.register.postRegisterUrl ?: defaultTargetUrl
    }

    def forgotPassword = {

        if (!request.post) {
            // show the form
            redirect (controller:'login', action:'auth', params:[active:3])
        }

        String username = params.username
        if (!username) {
            flash.error = message(code: 'spring.security.ui.forgotPassword.username.missing')
            redirect (controller:'login', action:'auth', params:[active:3])
        }

        def user = lookupUserClass().findByUsername(username)
        if (!user) {
            flash.error = message(code: 'spring.security.ui.forgotPassword.user.notFound')
            redirect (controller:'login', action:'auth', params:[active:3])
        }

        def registrationCode = new RegistrationCode(username: user.username).save()

        String url = generateLink('resetPassword', [t: registrationCode.token])

        def conf = SpringSecurityUtils.securityConfig
        def body = conf.ui.forgotPassword.emailBody
        if (body.contains('$')) {
            body = evaluate(body, [user: user, url: url])
        }
        mailService.sendMail {
            to user.username
            from conf.ui.forgotPassword.emailFrom
            subject conf.ui.forgotPassword.emailSubject
            html body.toString()
        }
        redirect (controller:'login', action:'auth', params:[active:3, emailSent: true])
    }

    def resetPassword = { ResetPasswordCommand command ->

        String token = params.t

        def registrationCode = token ? RegistrationCode.findByToken(token) : null
        if (!registrationCode) {
            flash.error = message(code: 'spring.security.ui.resetPassword.badCode')
            redirect uri: SpringSecurityUtils.securityConfig.successHandler.defaultTargetUrl
            return
        }

        if (!request.post) {
            return [token: token, command: new ResetPasswordCommand()]
        }

        command.username = registrationCode.username
        command.validate()

        if (command.hasErrors()) {
            return [token: token, command: command]
        }

        String salt = saltSource instanceof NullSaltSource ? null : registrationCode.username
        RegistrationCode.withTransaction { status ->
            def user = lookupUserClass().findByUsername(registrationCode.username)
            user.password = command.password
            user.save()
            registrationCode.delete()
        }

        springSecurityService.reauthenticate registrationCode.username

        flash.message = message(code: 'spring.security.ui.resetPassword.success')

        def conf = SpringSecurityUtils.securityConfig
        String postResetUrl = conf.ui.register.postResetUrl ?: conf.successHandler.defaultTargetUrl
        redirect uri: postResetUrl
    }
   
    protected String generateLink(String action, linkParams) {
        createLink(base: "$request.scheme://$request.serverName:$request.serverPort$request.contextPath",
            controller: 'register', action: action,
            params: linkParams)

    }

    protected String evaluate(s, binding) {
        new SimpleTemplateEngine().createTemplate(s).make(binding)
    }

    static final passwordValidator = { String password, command ->
        if (command.username && command.username.equals(password)) {
            return 'command.password.error.username'
        }

        def conf = SpringSecurityUtils.securityConfig

        int minLength = (conf.ui.password.minLength)? conf.ui.password.minLength : 6
        int maxLength = (conf.ui.password.maxLength)? conf.ui.password.maxLength : 64

        def passValidationRegex = (conf.ui.password.validationRegex)? conf.ui.password.validationRegex : '^.*(?=.*\\d)(?=.*[a-zA-Z])(?=.*[-!@#$%&*()_+=]).*$'	

        if (password && (password.length() < minLength || password.length() > maxLength || !password.matches(passValidationRegex)) ) {
            return 'command.password.error.strength'
        }
    }

    static final password2Validator = { value, command ->
        if (command.password != command.password2) {
            return 'command.password2.error.mismatch'
        }
    }
        
    static final postalValidator = { String postal, command ->
        if (postal.length()< 6 || postal.length()> 10 || !postal.isNumber()) {
            return 'command.postal.matches.invalid'
        }
    }
        
    static final phonenumberValidator = { String number, command ->
        if (number.length()< 6 || number.length()> 20 || !number.isNumber()) {
            return 'command.phonenumber.matches.invalid'
        }
    }
}

class RegisterCommand {

    String username
    String email
    String password
    String password2
    String name
    String lastname
    String address1
    String address2
    String postal
    String phonenumber

    static constraints = {
        username blank: false, email: true
        email blank: false, email: true, validator: { value, command ->
            if (value) {
                def User = AH.application.getDomainClass(
                    SpringSecurityUtils.securityConfig.userLookup.userDomainClassName).clazz
                if (User.findByUsername(value)) {
                    return 'registerCommand.username.unique'
                }
            }
        }
        password blank: false, validator: RegisterController.passwordValidator
        password2 validator: RegisterController.password2Validator
        name blank: false, maxSize: 20
        lastname blank: false, maxSize: 20
        address1 blank: false
        postal blank: false, validator: RegisterController.postalValidator
        phonenumber blank: false, validator: RegisterController.phonenumberValidator
    }
}

class ResetPasswordCommand {
    String username
    String password
    String password2

    static constraints = {
        password blank: false, validator: RegisterController.passwordValidator
        password2 validator: RegisterController.password2Validator
    }

}
